// Copyright 2022 AndeyaLee. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package middleware

import (
	"context"
	"strings"
	"sync"
	"time"
)

// MiddlewareManager manages middleware chains and provides common middleware
type MiddlewareManager struct {
	globalMiddleware []HandlerFunc
	namedMiddleware  map[string]HandlerFunc
	mu               sync.RWMutex
}

// NewMiddlewareManager creates a new middleware manager
func NewMiddlewareManager() *MiddlewareManager {
	return &MiddlewareManager{
		globalMiddleware: make([]HandlerFunc, 0),
		namedMiddleware:  make(map[string]HandlerFunc),
	}
}

// AddGlobal adds a global middleware
func (m *MiddlewareManager) AddGlobal(middleware HandlerFunc) {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.globalMiddleware = append(m.globalMiddleware, middleware)
}

// AddNamed adds a named middleware
func (m *MiddlewareManager) AddNamed(name string, middleware HandlerFunc) {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.namedMiddleware[name] = middleware
}

// GetNamed retrieves a named middleware
func (m *MiddlewareManager) GetNamed(name string) (HandlerFunc, bool) {
	m.mu.RLock()
	defer m.mu.RUnlock()
	middleware, exists := m.namedMiddleware[name]
	return middleware, exists
}

// GetGlobal returns all global middleware
func (m *MiddlewareManager) GetGlobal() []HandlerFunc {
	m.mu.RLock()
	defer m.mu.RUnlock()
	return append([]HandlerFunc(nil), m.globalMiddleware...)
}

// ClearGlobal clears all global middleware
func (m *MiddlewareManager) ClearGlobal() {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.globalMiddleware = m.globalMiddleware[:0]
}

// ClearNamed clears all named middleware
func (m *MiddlewareManager) ClearNamed() {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.namedMiddleware = make(map[string]HandlerFunc)
}

// ClearAll clears all middleware
func (m *MiddlewareManager) ClearAll() {
	m.ClearGlobal()
	m.ClearNamed()
}

// Built-in middleware functions

// LoggerMiddleware provides request logging
func LoggerMiddleware() HandlerFunc {
	return func(ctx *Context) error {
		start := time.Now()
		path := ctx.Path()
		method := ctx.Method()

		// Process request
		ctx.Next()

		// Log after processing
		latency := time.Since(start)
		status := ctx.Status()
		size := ctx.Size()

		ctx.Log().Infof("[%s] %s %s %d %d %v %s",
			method, path, ctx.RealIP(), status, size, latency, ctx.UserAgent())

		return nil
	}
}

// RecoveryMiddleware provides panic recovery
func RecoveryMiddleware() HandlerFunc {
	return func(ctx *Context) error {
		defer func() {
			if r := recover(); r != nil {
				// Convert panic to error
				var err error
				switch v := r.(type) {
				case error:
					err = v
				case string:
					err = NewError(ErrorCodeValidationError, v)
				default:
					err = NewError(ErrorCodeValidationError, "panic occurred")
				}

				// Handle the error
				HandleStructuredError(ctx, err)
			}
		}()

		ctx.Next()
		return nil
	}
}

// TimeoutMiddleware provides request timeout
func TimeoutMiddleware(timeout time.Duration) HandlerFunc {
	return func(ctx *Context) error {
		// Create timeout context
		timeoutCtx, cancel := context.WithTimeout(ctx.R.Context(), timeout)
		defer cancel()

		// Update request with timeout context
		ctx.R = ctx.R.WithContext(timeoutCtx)

		// Process request
		ctx.Next()

		// Check if timeout occurred
		select {
		case <-timeoutCtx.Done():
			if timeoutCtx.Err() == context.DeadlineExceeded {
				HandleStructuredError(ctx, NewError(ErrorCodeValidationError, "request timeout"))
			}
		default:
		}

		return nil
	}
}

// CORSMiddleware provides CORS support
func CORSMiddleware(origins []string, methods []string, headers []string) HandlerFunc {
	return func(ctx *Context) error {
		origin := ctx.HeaderParam("Origin")

		// Check if origin is allowed
		allowed := false
		if len(origins) == 0 {
			allowed = true // Allow all origins
		} else {
			for _, allowedOrigin := range origins {
				if allowedOrigin == "*" || allowedOrigin == origin {
					allowed = true
					break
				}
			}
		}

		if allowed {
			ctx.SetHeader("Access-Control-Allow-Origin", origin)
			ctx.SetHeader("Access-Control-Allow-Credentials", "true")

			if len(methods) > 0 {
				ctx.SetHeader("Access-Control-Allow-Methods", strings.Join(methods, ", "))
			}

			if len(headers) > 0 {
				ctx.SetHeader("Access-Control-Allow-Headers", strings.Join(headers, ", "))
			}
		}

		// Handle preflight requests
		if ctx.Method() == MethodOPTIONS {
			ctx.W.WriteHeader(StatusOK)
			return nil
		}

		ctx.Next()
		return nil
	}
}

// RateLimitMiddleware provides rate limiting
func RateLimitMiddleware(requests int, window time.Duration) HandlerFunc {
	// Simple in-memory rate limiter
	// In production, use Redis or similar
	clients := make(map[string][]time.Time)
	mu := sync.Mutex{}

	return func(ctx *Context) error {
		clientIP := ctx.RealIP()
		now := time.Now()

		mu.Lock()
		defer mu.Unlock()

		// Clean old requests
		cutoff := now.Add(-window)
		clientRequests := clients[clientIP]
		var validRequests []time.Time
		for _, reqTime := range clientRequests {
			if reqTime.After(cutoff) {
				validRequests = append(validRequests, reqTime)
			}
		}
		clients[clientIP] = validRequests

		// Check rate limit
		if len(validRequests) >= requests {
			HandleStructuredError(ctx, NewError(ErrorCodeValidationError, "rate limit exceeded"))
			return nil
		}

		// Add current request
		clients[clientIP] = append(validRequests, now)

		ctx.Next()
		return nil
	}
}

// SecurityMiddleware provides security headers
func SecurityMiddleware() HandlerFunc {
	return func(ctx *Context) error {
		// Set security headers
		ctx.SetHeader("X-Content-Type-Options", "nosniff")
		ctx.SetHeader("X-Frame-Options", "DENY")
		ctx.SetHeader("X-XSS-Protection", "1; mode=block")
		ctx.SetHeader("Referrer-Policy", "strict-origin-when-cross-origin")
		ctx.SetHeader("Content-Security-Policy", "default-src 'self'")

		ctx.Next()
		return nil
	}
}

// CompressionMiddleware provides response compression
func CompressionMiddleware() HandlerFunc {
	return func(ctx *Context) error {
		// Check if client accepts gzip
		acceptEncoding := ctx.HeaderParam("Accept-Encoding")
		if !strings.Contains(acceptEncoding, "gzip") {
			ctx.Next()
			return nil
		}

		// Set compression header
		ctx.SetHeader("Content-Encoding", "gzip")

		ctx.Next()
		return nil
	}
}

// RequestIDMiddleware adds a unique request ID
func RequestIDMiddleware() HandlerFunc {
	return func(ctx *Context) error {
		// Generate or use existing request ID
		requestID := ctx.HeaderParam("X-Request-ID")
		if requestID == "" {
			requestID = generateRequestID()
		}

		ctx.SetHeader("X-Request-ID", requestID)
		ctx.SetData("request_id", requestID)

		ctx.Next()
		return nil
	}
}

// MetricsMiddleware provides request metrics
func MetricsMiddleware() HandlerFunc {
	return func(ctx *Context) error {
		start := time.Now()
		method := ctx.Method()
		path := ctx.Path()

		// Process request
		ctx.Next()

		// Record metrics
		latency := time.Since(start)
		status := ctx.Status()

		// In production, send to metrics system
		ctx.Log().Infof("METRICS: %s %s %d %v", method, path, status, latency)

		return nil
	}
}

// generateRequestID generates a unique request ID
func generateRequestID() string {
	// Simple implementation - in production use UUID
	return time.Now().Format("20060102150405") + "-" + randomString(8)
}

// randomString generates a random string of given length
func randomString(length int) string {
	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	b := make([]byte, length)
	for i := range b {
		b[i] = charset[time.Now().UnixNano()%int64(len(charset))]
	}
	return string(b)
}
